#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <linux/fb.h>

#define RED   2
#define GREEN 1
#define BLUE  0


struct image_info
{
	int width;
	int height;
	int bpp; // bits per pixel 即每个像素点有几位，即色深
};

struct header
{
	int16_t type;
	int32_t size; // 图像文件大小
	int16_t reserved1;
	int16_t reserved2;
	int32_t offbits; // bmp图像数据偏移量

}__attribute__((packed));

struct info
{
	int32_t size; // 本结构大小    
	int32_t width; // 图像宽度（单位像素）
	int32_t height; // 图像高度（单位像素）
	int16_t planes; 
	int16_t bit_count; // 色深
	int32_t compression; // 是否压缩
	int32_t size_img;
	int32_t X_pel;
	int32_t Y_pel;
	int32_t clrused;
	int32_t clrImportant;
}__attribute__((packed));



char *init_lcd(struct fb_var_screeninfo *vinfo)      //打开显示屏，并初始化
{
 	int lcd = open("/dev/fb0",O_RDWR);
	if(lcd == -1)
	{
		perror("lcd open() is:");
		exit(0);
	}
	bzero(vinfo,sizeof(struct fb_var_screeninfo));
	ioctl(lcd, FBIOGET_VSCREENINFO, vinfo);
	char *fb = mmap(NULL, vinfo->xres * vinfo->yres * vinfo->bits_per_pixel/8,PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0);
	if(fb == MAP_FAILED)
	{
		perror("mmap() failed");
		exit(0);
	}
	return fb;
}
char * load_bmp(char *img,struct image_info *imginfo)    //对bmp图片进行解析，获取图片信息   返回图片信息
{
	int image = open(img,O_RDWR);
	if(image == -1)
	{
		printf("%s open() is %s\n",img,strerror(errno));
		exit(0);
	}

	struct header head;
	struct info info;
	if( read(image, &head, sizeof(struct header)) == -1)
	{
		perror("read image head is");
		exit(0);
	}
	if ( read(image, &info, sizeof (struct info)) == -1)
	{
		perror("read image info is");
		exit(0);
	}
	if(info.compression)
	{
		printf("the image not supported \n");
		exit(0);
	}
	imginfo->width =info.width;
	imginfo->height =info.height;
	imginfo->bpp =info.bit_count;
	
	char *imgdata = calloc(1, head.size-head.offbits);
	
	int read_bytes = head.size-head.offbits;
	int n;
	char *tmp = imgdata;
	while(read_bytes > 0)
	{
			n = read(image, tmp, read_bytes);
			tmp += n;
			read_bytes -= n;
	}
	return imgdata;

       	
}
void display_bmp(char * fb, struct fb_var_screeninfo *vinfo, 
	         char *img, struct image_info *imginfo,
		 int x,int y)               // 将图片显示在显示屏
{
	int pad = (4 - (imginfo->width * imginfo->bpp/8) % 4) % 4;   //没有到4个字节计算差几个字节

	fb += y * vinfo->xres * vinfo->bits_per_pixel/8 + x * vinfo->bits_per_pixel/8;   //计算初始位置

	img += (imginfo->width * imginfo->bpp/8 + pad) * (imginfo->height-1);
	
	int i,j;
	for(i=0; i<imginfo->height && i<vinfo->yres-y; i++)
	{
		long fb_offset,img_offset;
		for(j=0; j<imginfo->width && j<vinfo->xres-x; j++)
		{
			fb_offset = i * vinfo->xres * vinfo->bits_per_pixel/8 
				+   j * vinfo->bits_per_pixel/8;

			img_offset = j * imginfo->bpp/8;
			memcpy(fb + fb_offset + vinfo->red.offset/8, img + img_offset +RED, 1);
			memcpy(fb + fb_offset + vinfo->green.offset/8, img + img_offset +GREEN, 1);
			memcpy(fb + fb_offset + vinfo->blue.offset/8, img + img_offset +BLUE, 1);

		}
		img-= imginfo->width * imginfo->bpp/8 +pad;
	}

}



void bmp_show(char *bmp, char *FB,struct fb_var_screeninfo *vinfo,
		int x, int y)  //解析bmp图片同时显示在显示屏
{
	
	struct image_info imginfo;
	char *imgdata = load_bmp(bmp, &imginfo);
	display_bmp(FB, vinfo, imgdata, &imginfo, x, y);
}


